package com.info.provider.service.wechat.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.info.provider.entity.UserInfo;
import com.info.provider.redis.RedisUtil;
import com.info.provider.service.UserInfoService;
import com.info.provider.service.wechat.WeChatInteract;
import com.info.provider.utils.AESUtil;
import com.mars.app.exception.ErrorCodeMsg;
import com.mars.app.exception.WeChatInteractException;
import com.mars.app.utils.MarsAssert;
import com.mars.app.wetchat.WeChatIdentify;
import com.mars.app.wetchat.WeChatInteractInfo;
import com.mars.app.wetchat.WeChatStoreMsg;
import com.mars.app.wetchat.WeChatMsg;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * Created by pengshuo on 2018/1/8 14:47
 * Description:
 * Modified By:
 * Version:
 *
 * @author pengshuo
 */
@Service
public class WeChatInteractImpl implements WeChatInteract{

    private Logger logger = LogManager.getLogger(this.getClass());

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserInfoService userInfoService;

    /** 接口地址 */
    @Value("${wechat.auth.url}")
    private String url;

    /** 小程序唯一标识 */
    @Value("${wechat.auth.appid}")
    private String appid;

    /** 小程序的 app secret */
    @Value("${wechat.auth.secret}")
    private String secret;

    /** 固定填写为 authorization_code */
    @Value("${wechat.auth.grant_type}")
    private String grant_type;

    @Value("${wechat.auth.expire_time}")
    private long expire_time;

    /**
     * code 换取 session_key
     * {
     * ​这是一个 HTTPS 接口，开发者服务器使用登录凭证 code 获取 session_key 和 openid。
     * session_key 是对用户数据进行加密签名的密钥。为了自身应用安全，session_key 不应该在网络上传输。
     * }
     * @param js_code 登录时获取的 code
     * @return
     * @throws WeChatInteractException
     */
    @Override
    public WeChatIdentify code2SessionKey(String js_code) throws WeChatInteractException{
        try {
            StringBuilder sb = new StringBuilder(url);
            sb.append("?").append(WeChatMsg.APPID).append("=").append(appid);
            sb.append("&").append(WeChatMsg.SECRET).append("=").append(secret);
            sb.append("&").append(WeChatMsg.JS_CODE).append("=").append(js_code);
            sb.append("&").append(WeChatMsg.GRANT_TYPE).append("=").append(grant_type);
            /**
             * 获取用户的openid，参数一定要拼在url连接上，
             * 否则会报{"errcode":40013,"errmsg":"invalid appid, hints: [ req_id: iil1ba0504ns86 ]"}错误
             */
            return objectMapper.readValue(restTemplate.getForEntity(sb.toString(),String.class).getBody(),WeChatIdentify.class);
        }catch (Exception e){
            logger.error("code 换取 session_key Exception: {}",e);
            throw new WeChatInteractException(ErrorCodeMsg.WECHAT_INTERACT_ERROR,e);
        }
    }

    /**
     * 通过微信接口交互数据 生成本地的 sessionId
     *
     * @param identify 微信接口交互数据
     * @return
     * @throws WeChatInteractException
     */
    @Override
    public WeChatInteractInfo createThirdSession(WeChatIdentify identify) throws WeChatInteractException {
        try {
            // 生成 thirdSessionId
            String thirdSessionId = RandomStringUtils.randomAlphanumeric(64);
            // 存储在缓存
            redisUtil.add(
                    thirdSessionId,
                    expire_time,
                    new WeChatStoreMsg(identify.getOpenid(),identify.getSession_key(),identify.getUnionid())
            );
            // store openId
            userInfoService.create(new UserInfo(identify.getOpenid()));
            // return
            return new WeChatInteractInfo(thirdSessionId);
        }catch (Exception e){
            logger.error("生成 thirdSessionKey Exception: {}",e);
            throw new WeChatInteractException(ErrorCodeMsg.WECHAT_STORE_SESSION_ERROR,e);
        }
    }

    /**
     * 处理微信用户敏感信息
     *
     * @param interactInfo 敏感数据
     * @return 成功/失败
     * @throws WeChatInteractException
     */
    @Override
    public Boolean processEncryptedData(WeChatInteractInfo interactInfo) throws WeChatInteractException {
        Boolean redis;
        UserInfo result;
        try {
            // 获取缓存中的微信sessionKey
            Object storeInfo = redisUtil.get(interactInfo.getThirdSessionId());
            // check storeInfo
            MarsAssert.check(storeInfo != null,new WeChatInteractException(ErrorCodeMsg.LOCAL_CACHE_EXPIRE));
            // sessionKey
            WeChatStoreMsg sessionKey = objectMapper.readValue((String) storeInfo,WeChatStoreMsg.class);
            // encrypted data
            String encryptedData = AESUtil.decryptEncryptedData(sessionKey.getSession_key(),interactInfo.getEncryptedData(),interactInfo.getIv());

            logger.debug("解密数据 {}",encryptedData);

            UserInfo userInfo = objectMapper.readValue(encryptedData,UserInfo.class);
            // 跟新缓存
            sessionKey.setWatermark(userInfo.getWatermark());
            redis = redisUtil.update(interactInfo.getThirdSessionId(),sessionKey);
            // 保存数据
            result = userInfoService.createOrUpdate(userInfo);
        }catch (Exception e){
            logger.error("处理微信用户敏感信息 Exception: {}",e);
            throw new WeChatInteractException(ErrorCodeMsg.WECHAT_DECODE_ERROR,e);
        }
        return redis && result != null;
    }
}
